// 接口是局部导出  as是别名
import {doLogin as doLoginApi} from '@/services/login';
import { message } from 'antd';
// Reducer类型已经在umi中进行定义了
// 注意:假如没有例子,如何做类型推导? 思考某个对象是从哪来的,那在实现的时候肯定就定义了类型,除非它不是用ts开发的.
import type {Reducer, Effect, Subscription} from 'umi';
import {setToken,saveUserInfo} from '@/utils/myAuth';
import {clearUserCache} from '@/utils/myAuth';
import { history } from 'umi';
// 单独定义userInfo类型
export type TUserInfo = {
  username: string;
  password: string;
  nickname: string;
  icon: string;
  personalizedSignature?: null;
}
// 因为在 index.tsx中 mapStateToProps 中的state实际上就是这个state
export type MStateType = {
  foo: string;
  userInfo: TUserInfo | {};  
}
// 对M进行类型定义
type MType = {
  namespace: string;
  state: MStateType,
  reducers: {
    initUserInfo: Reducer;
  },
  effects: {
    doLogin: Effect;
    doLogout: Effect;
  },
  subscriptions: {
    foo: Subscription;
    bar: Subscription;
  }
}

// 理解为创建了一个vuex文件
const M: MType = {
  namespace: 'myLogin',
  // state是不允许被直接修改的,需要通过reducer进行修改
  // 这个和vuex中的mountion是一样
  state: {
    foo: 'ff',
    userInfo: {}
  },
  // 用于对state进行修改
  reducers: {
    // reducer(state,{payload}) 语法 state用于合并最新的state对象
    // reducer需要返回一个全新的 state,不能是原来的state,必须是全新的!
    initUserInfo(state,{payload}){
      const {userInfo} = payload;
      return {
        ...state, // 解构原来的state,保证不丢失信息
        userInfo
      }
    },
    // // 用于处理退出
    // doLogout(state){
    //   // 1. 清除store
    //   const userInfo = {}
    //   // 2. 清除localstorage
    //   clearUserCache();
    //   return{
    //     ...state,
    //     userInfo
    //   }
    // }
  },
  // 异步请求,和vuex的action一样. 获取到结果需要调用reducer进行state的修改
  effects: {
    // 声明effect 用于处理异步请求
    // 1. 形参action是传递的载荷对象(vuex理解)
    // 2. 形参{call,put}是固定写法,不用纠结
    // * 函数添加*声明,会把当前函数变成一个生成器 
    // 生成器: # https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator
    // yield # https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/yield
    // 作用生成器Generator 用于把函数变为可控制的运行状态,配合迭代器使用. 如果没有迭代器,代码将会挂起到yield的地方.必须通过next()才能走到下一步.
    // async await是生成器和迭代器的语法糖
    // 这里为什么使用 yield ,是为了把异步请求"压扁"为"同步"代码.道理和async..await是一样的.
    *doLogin({payload}, {call,put}){
      // console.log('payload',payload);
      // 发送请求 yield挂起,可以把异步变为同步
      // message与引入模块冲突 重命名为: errMsg
      const {data,success,message:errMsg} = yield call(doLoginApi,payload);
      // console.log('response',response);
      if(success){
        // 提示成功!
        message.success('🎉 🎉 🎉  登录成功！');
        // 把内容存入state 使用put调用reducer
        yield put({
          type: 'initUserInfo',
          payload: data
        })
        // 因为data之前在接口返回值里没有确定类型所以这里可能提示无法判断 可以采用断言类型
        // const myData = data as {token: string,userInfo: TUserInfo};
        const {token,userInfo} = data;
        // 存储token和userInfo
        setToken(token);
        saveUserInfo(userInfo as TUserInfo)
        // 刷新跳转到首页
        window.location.href = '/';
      }else{
        // 提示失败!
        message.error(errMsg)
      }
    },
    *doLogout(action, {put}){
        // 1. 初始化userInfo为{}
        yield put({
          type: 'initUserInfo',
          payload: {}
        })
        // 2. 清除cache
        clearUserCache();
        // 3. 跳转页面
        history.push('/user/login');
    }
  },
  // dva 自己加的 用于处理监听
  subscriptions: {
    // 函数的key随便起,在代码中不体现
    // 因为Subscription 语义是订阅, dva.start() 时被挨着执行 
    // # https://dvajs.com/api/#subscriptions
    // foo({dispatch,history}){
    foo(){
      // console.log('foo...')
      // 在dav启动的时候开启一个订阅,监听history的变化
      // history.listen((location, action) => {
      //   console.log(location.pathname);
      // });
    },
    bar(){
      // document.onclick = function(){
      //   console.log('dianjie...');
      // }
    }
  }
}
export default M;
